package com.unitd.filesystem.provider.impl;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectResult;
import com.unitd.filesystem.comm.FileItem;
import com.unitd.filesystem.comm.FileType;
import com.unitd.filesystem.provider.AbstractProvider;
import com.unitd.filesystem.utils.HttpDownloadHelper;
import com.unitd.frame.comm.utils.FileUtils;
import com.unitd.frame.comm.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.Random;

/**
 * @desc 与阿里云OSS分布式文件系统进行上传、下载处理的接口实现类
 * @filename AliyunossProvider.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/3/6
 */
public class AliyunossProvider extends AbstractProvider {

	private static Logger logger = LoggerFactory.getLogger(AliyunossProvider.class.getName());
	public static final String NAME = "aliyun";
	/** 文件存储目录 */
	private String filedir = "data/";
	/** 阿里云OSS客户端 */
	private OSSClient ossClient;

	public AliyunossProvider(String endpoint, String bucketName, String accessKey, String secretKey) {
		ossClient = new OSSClient(endpoint, accessKey, secretKey);
		this.bucketName = bucketName;
	}

	/**
	 * @param catalog  图片分组
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param file     需要上传的文件
	 * @return String
	 * @desc 文件方式上传
	 */
	@Override
	public String upload(String catalog, String fileName, File file) {
		try {
			// 获取需要上传的文件的文件名
			if(StringUtils.isNull(fileName)) {
				String fileExtName = FileUtils.getFileExt(file.getName());
				Random random = new Random();
				fileName = random.nextInt(10000) + System.currentTimeMillis() + fileExtName;
			}
			FileType fileType = FileType.getFileTypeBySuffix(FileUtils.getFileExt(fileName));

			// 上传文件到阿里云OSS
			uploadFile2OSS(catalog, fileName, new FileInputStream(file), fileType);

			return getFileRemoteUrl(fileName);
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
			return null;
		}
	}

	/**
	 * @desc 字节流方式上传
	 * @param catalog  图片分类
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param in       文件输入流
	 * @param fileType 文件类型信息
	 * @return String
	 */
	@Override
	public String upload(String catalog, String fileName, InputStream in, FileType fileType) {
		try {
			// 获取需要上传的文件的文件名
			if(StringUtils.isNull(fileName)) {
				Random random = new Random();
				fileName = random.nextInt(10000) + System.currentTimeMillis() + fileType.getSuffix();
			}

			// 上传文件到阿里云OSS
			uploadFile2OSS(catalog, fileName, in, fileType);

			return getFileRemoteUrl(fileName);
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
			return null;
		}
	}

	/**
	 * @param catalog  图片分组
	 * @param fileName 文件名(需确保唯一性),为空系统自动生成
	 * @param data     文件字节
	 * @param fileType 文件类型信息
	 * @return String
	 * @desc 字节方式上传
	 */
	@Override
	public String upload(String catalog, String fileName, byte[] data, FileType fileType) {
		try {
			// 获取需要上传的文件的文件名
			if(StringUtils.isNull(fileName)) {
				Random random = new Random();
				fileName = random.nextInt(10000) + System.currentTimeMillis() + fileType.getSuffix();
			}

			// 上传文件到阿里云OSS
			uploadFile2OSS(catalog, fileName, new ByteArrayInputStream(data), fileType);

			return getFileRemoteUrl(fileName);
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
			return null;
		}
	}

	/**
	 * @param catalog  图片分类
	 * @param fileName fileName 文件名(需确保唯一性),为空则自动生成
	 * @param origUrl  原始网络地址
	 * @return String
	 * @desc 从网络地址下载图片并上传
	 */
	@Override
	public String upload(String catalog, String fileName, String origUrl) {
		PutObjectResult result;
		try {
			// 读取互联网上指定路径的文件/图片
			FileItem fileItem = HttpDownloadHelper.read(origUrl);

			return this.upload(catalog, fileName, fileItem.getDatas(), fileItem.getFileType());
		} catch (Exception e) {
			logger.error(e.getMessage());
			processException(e);
			return null;
		}
	}

	@Override
	public boolean delete(String fileName) {
		ossClient.deleteObject(bucketName, fileName);
		return true;
	}

	/**
	 * @param fileId   分布式文件系统中的文件全路径
	 * @param destFile 下载的文件存放在本地的路径
	 * @desc 下载文件, 将分布式文件系统中的指定ID的文件下载到本地指定路径
	 */
	@Override
	public void download(String fileId, File destFile) {

	}

	/**
	 * @desc 创建上传文件锁, 用于防止上传过程中文件发生改变
	 * @param fileNames 需要上传的文件
	 * @return String
	 */
	@Override
	public String createUploadToken(String... fileNames) {
		return null;
	}

	@Override
	public void close() throws IOException {
		ossClient.shutdown();
	}

	@Override
	public String name() {
		return NAME;
	}

	/**
	 * @desc 上传到OSS服务器  如果同名文件会覆盖服务器上的
	 * @param bucketName 文件系统的所属空间名
	 * @param fileName 文件名称 包括后缀名
	 * @param instream 文件流
	 * @return 出错返回"" ,唯一MD5数字签名
	 */
	private String uploadFile2OSS(String bucketName, String fileName, InputStream instream, FileType fileType) {
		String ret = "";
		try {
			// 创建上传Object的Metadata
			ObjectMetadata objectMetadata = new ObjectMetadata();
			objectMetadata.setContentLength(instream.available());
			objectMetadata.setCacheControl("no-cache");
			objectMetadata.setHeader("Pragma", "no-cache");
			objectMetadata.setContentType(fileType.getMimeType());
			objectMetadata.setContentDisposition("inline;filename=" + fileName);

			// 上传文件
			PutObjectResult putResult = ossClient.putObject(bucketName, filedir + fileName, instream, objectMetadata);
			ret = putResult.getETag();
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		} finally {
			try {
				if (instream != null) {
					instream.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return ret;
	}

	/**
	 * @desc 获取上传后的文件存储在OSS的访问URL
	 * @param fileName 文件名
	 * @return
	 */
	public String getFileRemoteUrl(String fileName) {
		if (!StringUtils.isEmpty(fileName)) {
			String[] split = fileName.split("/");
			String key = this.filedir + split[split.length - 1];

			// 设置URL过期时间为10年  3600l* 1000*24*365*10
			Date expiration = new Date(System.currentTimeMillis() + 3600L* 1000 * 24 * 365 * 10);
			// 生成URL
			URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
			if (url != null) {
				return url.toString();
			}
		}
		return null;
	}
}